package com.view.speelveld;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.LinearGradient;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.Toast;

import com.example.project_speelveld.HighScoreActivity;
import com.example.project_speelveld.R;
import com.level.speelveld.Level;
import com.level.speelveld.Path;
import com.level.speelveld.Stone;
import com.model.speelveld.GameModel;
import com.model.speelveld.WaveTime;
import com.monster.speelveld.BossMonster;
import com.monster.speelveld.Monster;
import com.monster.speelveld.NormalMonster;
import com.monster.speelveld.SlowMonster;
import com.monster.speelveld.fastMonster;
import com.tower.speelveld.AirTower;
import com.tower.speelveld.EarthTower;
import com.tower.speelveld.FireTower;
import com.tower.speelveld.Tower;
import com.tower.speelveld.WaterTower;


// TODO: Auto-generated Javadoc
/**
 * The Class GameView.
 */
public class GameView extends TileView implements Observer {
	
	/** The game board. */
	private GameObject [][] gameBoard;
	
	/** The Constant NROFDRAWABLES. */
	private static final int NROFDRAWABLES = 19;
	
	/** The Constant FIRETOWER. */
	public static final int FIRETOWER = 1;
	
	/** The Constant WATERTOWER. */
	public static final int WATERTOWER = 2;
	
	/** The Constant AIRTOWER. */
	public static final int AIRTOWER = 3;
	
	/** The Constant EARTHTOWER. */
	public static final int EARTHTOWER = 4;
	
	/** The Constant NORMAL_MONSTER_HEALTH_FULL. */
	public static final int NORMAL_MONSTER_HEALTH_FULL = 5;
	
	/** The Constant NORMAL_MONSTER_HEALTH_MEDIUM. */
	public static final int NORMAL_MONSTER_HEALTH_MEDIUM = 6;
	
	/** The Constant NORMAL_MONSTER_HEALTH_LOW. */
	public static final int NORMAL_MONSTER_HEALTH_LOW = 7;
	
	/** The Constant FAST_MONSTER_HEALTH_FULL. */
	public static final int FAST_MONSTER_HEALTH_FULL = 8;
	
	/** The Constant FAST_MONSTER_HEALTH_MEDIUM. */
	public static final int FAST_MONSTER_HEALTH_MEDIUM = 9;
	
	/** The Constant FAST_MONSTER_HEALTH_LOW. */
	public static final int FAST_MONSTER_HEALTH_LOW = 10;
	
	/** The Constant SLOW_MONSTER_HEALTH_FULL. */
	public static final int SLOW_MONSTER_HEALTH_FULL = 11;
	
	/** The Constant SLOW_MONSTER_HEALTH_MEDIUM. */
	public static final int SLOW_MONSTER_HEALTH_MEDIUM = 12;
	
	/** The Constant SLOW_MONSTER_HEALTH_LOW. */
	public static final int SLOW_MONSTER_HEALTH_LOW = 13;
	
	/** The Constant BOSS_MONSTER_HEALTH_FULL. */
	public static final int BOSS_MONSTER_HEALTH_FULL = 14;
	
	/** The Constant BOSS_MONSTER_HEALTH_MEDIUM. */
	public static final int BOSS_MONSTER_HEALTH_MEDIUM = 15;
	
	/** The Constant BOSS_MONSTER_HEALTH_LOW. */
	public static final int BOSS_MONSTER_HEALTH_LOW = 16;
	
	/** The Constant PATH. */
	public static final int PATH = 17;
	
	/** The Constant CELL. */
	public static final int CELL = 18;
	
	/** The Constant STONE. */
	public static final int STONE = 19;
	
	/** The to add. */
	public static int toAdd = CELL;
	
	/** The to spawn. */
	public static int toSpawn = NORMAL_MONSTER_HEALTH_FULL;
	
	/** The naam id. */
	private final int NAAM_ID = 1;
	
	/** The monster spawned. */
	private int monsterSpawned = 0;
	
	/** The wave done. */
	private boolean waveDone = false;
	
	/** The game model. */
	private GameModel gameModel;
	
	/** The route. */
	private Level route;
	
	/** The level code. */
	private int levelCode;
	
	/** The wave timer. */
	private WaveTime waveTimer;
	
	/** The update status bar. */
	public static boolean updateStatusBar = false;
	
	/** The gameview. */
	private GameView gameview = this;
	
	/** The monsters on field. */
	private ArrayList<Monster> monstersOnField = new ArrayList<Monster>();
	
	/** The next wave button. */
	private Button nextWaveButton;
	
	/** The mt. */
	private MonsterTimer mt;
	
	/** The special attack button. */
	private Button specialAttackButton;
	
	/** The temp monsters spawned. */
	private int tempMonstersSpawned;
	
	/**
	 * Instantiates a new game view.
	 *
	 * @param context the context
	 * @param attrs the attrs
	 */
	public GameView(Context context, AttributeSet attrs) {
		super(context, attrs);
//		initGameView();
	}
	
	/**
	 * Instantiates a new game view.
	 *
	 * @param context the context
	 * @param attrs the attrs
	 * @param defStyle the def style
	 */
	public GameView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
//        initGameView();
	}

	/**
	 * Inits the game view.
	 */
	public void initGameView() {
		waveTimer = new WaveTime(5000);
		setFocusable(true);
		waveTimer.addObserver(this);
		initNewGame();
		
	}
	
	/**
	 * Sets the level.
	 *
	 * @param levelCode the new level
	 */
	public void setLevel(int levelCode)
	{
		this.levelCode = levelCode;
	}
	
	/**
	 * Sets the game model.
	 *
	 * @param gameModel the new game model
	 */
	public void setGameModel(GameModel gameModel)
	{
		this.gameModel = gameModel;
	}
	

	/**
	 * Inits the new game.
	 */
	private void initNewGame() {
		gameBoard = new GameObject [mXTileCount][mYTileCount];
		for (int i = 0 ; i< mXTileCount; i++)
			for (int j = 0; j < mYTileCount; j++)
			{
			
					gameBoard[i][j] = new GameObject(CELL);
				
				
			}
				
		resetTiles(NROFDRAWABLES +1);
		Resources r = this.getContext().getResources();

		resetTiles(NROFDRAWABLES + 1); /* zero (0) is used for empty */
		
		loadTile(FIRETOWER , r.getDrawable(R.drawable.fire));
		loadTile(WATERTOWER, r.getDrawable(R.drawable.water));
		loadTile(AIRTOWER, r.getDrawable(R.drawable.air));
		loadTile(EARTHTOWER, r.getDrawable(R.drawable.earth));
		
		loadTile(NORMAL_MONSTER_HEALTH_FULL, r.getDrawable(R.drawable.normal_monster_health_full));
		loadTile(NORMAL_MONSTER_HEALTH_MEDIUM, r.getDrawable(R.drawable.normal_monster_health_medium));
		loadTile(NORMAL_MONSTER_HEALTH_LOW, r.getDrawable(R.drawable.normal_monster_health_low));
		
		loadTile(FAST_MONSTER_HEALTH_FULL, r.getDrawable(R.drawable.fast_monster_health_full));
		loadTile(FAST_MONSTER_HEALTH_MEDIUM, r.getDrawable(R.drawable.fast_monster_health_medium));
		loadTile(FAST_MONSTER_HEALTH_LOW, r.getDrawable(R.drawable.fast_monster_health_low));
		
		loadTile(SLOW_MONSTER_HEALTH_FULL, r.getDrawable(R.drawable.slow_monster_health_full));
		loadTile(SLOW_MONSTER_HEALTH_MEDIUM, r.getDrawable(R.drawable.slow_monster_health_medium));
		loadTile(SLOW_MONSTER_HEALTH_LOW, r.getDrawable(R.drawable.slow_monster_health_low));
		
		loadTile(BOSS_MONSTER_HEALTH_FULL, r.getDrawable(R.drawable.boss_monster_health_full));
		loadTile(BOSS_MONSTER_HEALTH_MEDIUM, r.getDrawable(R.drawable.boss_monster_health_medium));
		loadTile(BOSS_MONSTER_HEALTH_LOW, r.getDrawable(R.drawable.boss_monster_health_low));
		
		loadTile(CELL, r.getDrawable(R.drawable.grass));
		loadTile(PATH, r.getDrawable(R.drawable.path));
		loadTile(STONE, r.getDrawable(R.drawable.stone));
		
		route = new Level(levelCode);
		
		Tower_Menu towerMenu = (Tower_Menu)((Activity)getContext()).findViewById(R.id.towerMenu1);
		
		if (towerMenu != null)
		{
			towerMenu.setLevel(route);
		}	
		
		for(Path p: route.getRoute())
		{
			gameBoard[p.getX()][p.getY()] = p;
		}
		
		for(Stone s : route.getObstakels())
		{
			gameBoard[s.getX()][s.getY()] = s;
		}
		
		gameBoard[0][0] = new GameObject(CELL); /* just a start */
		nextWaveButton = (Button) ((Activity) getContext()).findViewById(R.id.nextwavebutton);
		specialAttackButton = (Button) ((Activity) getContext()).findViewById(R.id.specialattackbutton);
		
		

		updateField(); 
		invalidate(); 
		
		
	}
	
	/**
	 * Destroy game.
	 */
	public void destroyGame()
	{
		pauzeerSpel();
		waveTimer = null;
		monstersOnField = new ArrayList<Monster>();
	}
	
	/**
	 * Pauzeer spel.
	 */
	public void pauzeerSpel()
	{
		tempMonstersSpawned = monsterSpawned;
		if(mt != null)
		{
			mt.cancel();
		}
		waveTimer.stopTimer();
		for(Monster m : monstersOnField)
		{
			m.stopTimer();
		}
		
		for (int i = 0 ; i< mXTileCount; i++)
		{
			for (int j = 0; j < mYTileCount; j++)
			{
				if(gameBoard[i][j] instanceof Tower)
				{
					((Tower)gameBoard[i][j]).stopTimer();
				}
			}
		}
	}
	
	/**
	 * Vervolg spel.
	 */
	public void vervolgSpel()
	{
		for (int i = 0 ; i< mXTileCount; i++)
		{
			for (int j = 0; j < mYTileCount; j++)
			{
				if(gameBoard[i][j] instanceof Tower)
				{
					((Tower)gameBoard[i][j]).vervolgTimer();
				}
			}
		}
		
		for(Monster m : monstersOnField)
		{
			m.vervolgTimer();
		}
		waveTimer.vervolgTimer();
		if(monsterSpawned < 10 || (gameModel.getWaveNumber() == 4 && monsterSpawned == 0))
		{
			if(gameModel.getWaveNumber() == 4 && monsterSpawned == 1)
				mt = null;
			else
				mt = new MonsterTimer((10 - tempMonstersSpawned) * 1800, 1800);
			
			if(mt != null)
				mt.start();
		}
	}
	
	/**
	 * Update field.
	 */
	public void updateField() {
		if (gameModel == null || gameModel.getLives() > 0)
		{
		for (int i=0; i< mXTileCount; i++)
			for (int j = 0; j<mYTileCount; j++){
				if (gameBoard[i][j] == null) {
					setTile(CELL, i,j); /* background tile */
				} else {
					setTile(gameBoard[i][j].getTileId(),i,j);
				}
			}

		}
		else 
		{
			pauzeerSpel();
			
			AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
			builder.setTitle("Game Over");
			builder.setMessage("Highscore");
			
			final EditText naam = new EditText(getContext());
			naam.setId(NAAM_ID);
			builder.setView(naam);
			
			builder.setNegativeButton("Submit", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					//Highscore wegschrijven in textbestand
					FileOutputStream fos;
					String fileName = "HighScore.txt";
					String data = "";
					if (naam.getText().length() >0 )
					{
						data = naam.getText().toString() + "," +  gameModel.getScore() + "\r\n";
					}
					else 
					{
						data = "Geen naam ingevuld" + "," +  gameModel.getScore() + "\r\n";
					}
					
					try {
							File file = new File(getContext().getFilesDir() ,fileName);
							if (!file.exists()) {
							        try
							        {
								            file.createNewFile();
								        } catch (IOException e) {
								            e.printStackTrace();
							        }
							}
							fos = new  FileOutputStream(file, true);
						
						try {
							fos.write(data.getBytes());
							fos.close();
							//Log.d("MIKE IS PRO", "FileGeschrijft");
							Intent i  =  new Intent(((Activity)getContext()), HighScoreActivity.class);
							((Activity)getContext()).startActivity(i);
							
						} catch (IOException e) {
							Log.e("Controller",
									e.getMessage()
											+ e.getLocalizedMessage()
											+ e.getCause());
						}
					} catch (FileNotFoundException e1) {
						e1.printStackTrace();
						
					}
					
				}
			});
			// tijdelijk new game, wanneer een hoofdmenu komt, redirect deze knop je hierheen
			builder.setPositiveButton("New Game", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					
					gameModel.reset();
					destroyGame();
					initGameView();
					specialAttackButton.setClickable(true);
					
				}
				
				
			});
			builder.show();
		}
	}
	
	/* (non-Javadoc)
	 * @see com.view.speelveld.TileView#onSizeChanged(int, int, int, int)
	 */
	@Override
	public void onSizeChanged(int w, int h, int oldw, int oldh){
        super.onSizeChanged(w,h,oldw, oldh);
        initNewGame();
   }

	//@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	/* (non-Javadoc)
	 * @see com.view.speelveld.TileView#touched(int, int)
	 */
	@Override
	public void touched(int x, int y){
		//Als de locatie op het speelveld van het type Tower is.
		if(gameBoard[x][y] instanceof Tower)
		{
			
			Tower_Menu towerMenu = (Tower_Menu)((Activity)getContext()).findViewById(R.id.towerMenu1);
			towerMenu.setUpgradeMenuVisible((Tower)gameBoard[x][y]);
			toAdd = CELL;
		}
		else if (gameBoard[x][y] instanceof Stone || gameBoard[x][y] instanceof Path)
		{
			Toast toast = Toast.makeText(getContext(), "Mag niet!", Toast.LENGTH_SHORT);
			toast.show();
		}
		//Als de locatie op het speelveld van het type Level is.
		else if(!(gameBoard[x][y]  instanceof Path))
		{
			//Switch keuze uit welke toren die moet toevoegen.
			switch(toAdd)
			{
				//Als toAdd FIRETOWER/WATERTOWER/AIRTOWER/EARTHTOWER is dat respectievelijke toren toevoegen.
				case FIRETOWER:
					FireTower ft = new FireTower(x, y, 1,  100, 90, this.gameBoard);
					if(gameModel.buyCheck(ft.getValue()))
					{
						gameBoard[x][y] = ft;
						gameModel.removeMoney(ft.getValue());
						ft.setRangePath(route.getInRangePath(ft));
					}
					else
					{
						Toast toast = Toast.makeText(getContext(), "Need more money!", Toast.LENGTH_SHORT);
						toast.show();
					}
					break;
				case WATERTOWER: 
					WaterTower wt = new WaterTower(x, y, 1, 75, 68, this.gameBoard);
					if(gameModel.buyCheck(wt.getValue()))
					{
						gameBoard[x][y] = wt;
						gameModel.removeMoney(wt.getValue());
						wt.setRangePath(route.getInRangePath(wt));
					}
					else
					{
						Toast toast = Toast.makeText(getContext(), "Need more money!", Toast.LENGTH_SHORT);
						toast.show();
					}
					break;
				case AIRTOWER: 
					AirTower at = new AirTower(x, y, 2,  50, 45, this.gameBoard);
					if(gameModel.buyCheck(at.getValue()))
					{ 	
						gameBoard[x][y] = at;
						gameModel.removeMoney(at.getValue());
						at.setRangePath(route.getInRangePath(at));
					}
					else
					{
						Toast toast = Toast.makeText(getContext(), "Need more money!", Toast.LENGTH_SHORT);
						toast.show();
					}
					break;
				case EARTHTOWER: 
					EarthTower et = new EarthTower(x, y, 2, 125, 90, this.gameBoard);
					if(gameModel.buyCheck(et.getValue()))
					{
						gameBoard[x][y] = et;
						gameModel.removeMoney(et.getValue());
						et.setRangePath(route.getInRangePath(et));
					}
					else
					{
						Toast toast = Toast.makeText(getContext(), "Need more money!", Toast.LENGTH_SHORT);
						toast.show();
					}
					break;
				default : break;
			}
		}
		
		//Update en repaint.
		updateField(); /* update the view */
		invalidate(); /* tell Android the view has to be redrawn */
	}
	
	/**
	 * New wave.
	 */
	public void newWave()
	{
		waveDone = false;
		monsterSpawned = 0;
	
		//Als waveNumber 5 is.
		if(gameModel.getWaveNumber() == 4)
		{
			//Verhoog het level en reset waveNumber.
			gameModel.addLevel();
			specialAttackButton.setClickable(true);
			gameModel.resetWave();
		}
		
		//WaveNumber omhoog.
		gameModel.nextWave();
		
		//Switch op waveNumber
		switch(gameModel.getWaveNumber())
		{
			//Roteer tussen Normaal/snel/langzaam/boss monsters voordat het level omhoog gaat.
			case 1: 
				toSpawn = NORMAL_MONSTER_HEALTH_FULL;
				mt = new MonsterTimer(18000, 1800); 
				mt.start();
				specialAttackButton.setOnClickListener(new Ocl());
				break;
			case 2:
				toSpawn = FAST_MONSTER_HEALTH_FULL;
				mt = new MonsterTimer(18000, 1800); 
				mt.start();
				break;
			case 3:
				toSpawn = SLOW_MONSTER_HEALTH_FULL;
				mt = new MonsterTimer(18000, 1800); 
				mt.start();
				break;
			case 4:

				toSpawn = BOSS_MONSTER_HEALTH_FULL;
				mt = new MonsterTimer(0, 0); 
				mt.start();
				break;
			default: break;
		}
		
		//Update en repaint.
		updateField();
		postInvalidate();
		
	}
	
	/* (non-Javadoc)
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */
	@Override
	public void update(Observable o, Object arg1) {
		if(o instanceof WaveTime)
		{
			waveTimer.setTimeMs(60000);
			newWave();
		}
	}
	
	
	/**
	 * Move monster.
	 *
	 * @param m the m
	 */
	public void moveMonster(Monster m)
	{
		gameBoard[m.getPosition().getX()][m.getPosition().getY()] = m;
		gameBoard[route.getPrevPath(m.getPosition()).getX()][route.getPrevPath(m.getPosition()).getY()] = m.getPosition();
		
		updateField();
		invalidate();
	}
	
	/**
	 * Delete monster.
	 *
	 * @param m the m
	 */
	public void deleteMonster(Monster m)
	{

		if(m.getPosition() == null)
		{
			gameBoard[route.getEndPostion().getX()][route.getEndPostion().getY()] = route.getEndPostion();
		}
		else
		{
			gameBoard[m.getPosition().getX()][m.getPosition().getY()] = m.getPosition();
		}
		monstersOnField.remove(m);
		if(monstersOnField.size() == 0)
		{
			if(waveDone)
			{
				nextWaveButton.setClickable(true);
				nextWaveButton.setOnClickListener(new Ocl());
			}
			
		}
		
		m = null;
		updateField();
		invalidate();

	}
	
		/**
		 * The Class MonsterTimer.
		 */
		class MonsterTimer extends CountDownTimer
		{

			/**
			 * Instantiates a new monster timer.
			 *
			 * @param millisInFuture the millis in future
			 * @param countDownInterval the count down interval
			 */
			public MonsterTimer(long millisInFuture, long countDownInterval) {
				super(millisInFuture, countDownInterval);
			}

			/* (non-Javadoc)
			 * @see android.os.CountDownTimer#onFinish()
			 */
			@Override
			public void onFinish() {
				onTick(0);
				
			}

			/* (non-Javadoc)
			 * @see android.os.CountDownTimer#onTick(long)
			 */
			@Override
			public void onTick(long millisUntilFinished) {
				int x = route.getStartPosition().getX();
				int y = route.getStartPosition().getY();
				switch(toSpawn)
				{
					case NORMAL_MONSTER_HEALTH_FULL: 
						Monster nm = new NormalMonster(gameModel.getLevel(), gameview, route, gameModel);
						gameBoard[x][y] = nm;
						monstersOnField.add(nm);
						monsterSpawned++;
						if(monsterSpawned == 10)
						{
							waveDone = true;
						}
						break;
					case FAST_MONSTER_HEALTH_FULL: 
						Monster fm = new fastMonster(gameModel.getLevel(), gameview, route, gameModel);
						gameBoard[x][y] = fm;
						monstersOnField.add(fm);
						monsterSpawned++;
						if(monsterSpawned == 10)
						{
							waveDone = true;
						}
						break;
					case SLOW_MONSTER_HEALTH_FULL: 
						Monster sm = new SlowMonster(gameModel.getLevel(), gameview, route, gameModel);
						gameBoard[x][y] = sm;
						monstersOnField.add(sm);
						monsterSpawned++;
						if(monsterSpawned == 10)
						{
							waveDone = true;
						}
						break;
					case BOSS_MONSTER_HEALTH_FULL: 
						Monster bm = new BossMonster(gameModel.getLevel(), gameview, route, gameModel);
						gameBoard[x][y] = bm;
						monstersOnField.add(bm);
						monsterSpawned++;
						if(monsterSpawned == 1)
						{
							waveDone = true;
						}
						break;
						//case andere monsters spawnen
						default: break;
				}
				updateField();
				postInvalidate();
				
			}
			
		}
		
		/**
		 * The Class imageTimer.
		 */
		class imageTimer extends CountDownTimer
		{
			
			/** The g. */
			private RelativeLayout g;
			
			/** The image. */
			private ImageView image;

			/**
			 * Instantiates a new image timer.
			 *
			 * @param millisInFuture the millis in future
			 * @param countDownInterval the count down interval
			 */
			public imageTimer(long millisInFuture, long countDownInterval) {
				super(millisInFuture, countDownInterval);
				image = new ImageView(getContext());
				image.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
				image.setImageDrawable(getResources().getDrawable(R.drawable.bam));
				g = (RelativeLayout) ((Activity)getContext()).findViewById(R.id.relLayout);
				g.addView(image);
				
;				
			}

			/* (non-Javadoc)
			 * @see android.os.CountDownTimer#onFinish()
			 */
			@Override
			public void onFinish() {
				onTick(0);
				
			}

			/* (non-Javadoc)
			 * @see android.os.CountDownTimer#onTick(long)
			 */
			@Override
			public void onTick(long millisUntilFinished) {
				g.removeView(image);
				
			}
			
		}
		
		// de listenener voor de nextWave en special attack
		/**
		 * The Class Ocl.
		 */
		class Ocl implements OnClickListener
		{

			/* (non-Javadoc)
			 * @see android.view.View.OnClickListener#onClick(android.view.View)
			 */
			@Override
			public void onClick(View v) {
				if(v == nextWaveButton)
				{
					waveTimer.resetTimer();
					newWave();
					nextWaveButton.setClickable(false);
				}
 				if(v == specialAttackButton)
				{
 				 					
					for(int i = 0; i < monstersOnField.size();)
					{
						if (monstersOnField.get(i).getHealth() > (150 + (100 * gameModel.getLevel())))
						{
							monstersOnField.get(i).removeHealth(150 + (100 * gameModel.getLevel()));
							i++;
						}
						else
						{
							monstersOnField.get(i).removeHealth(150 + (100 * gameModel.getLevel()));
						}								
					}	
				
					specialAttackButton.setClickable(false);
					new imageTimer(1000, 1000).start();
				}
				
			}
			
		}
}


